<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>

	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<title>Insert title here</title>
	</head>

	<body>
		<h1>函数</h1>
		<p>
			函数挂载在一个对象上,作为对象的一个属性,那就成为对象的方法<br>
			通过对象调用此方法时,该对象就是此次调用的上下文(context),也就是该函数的this值<br>
			javascript中,函数即对,所以可以把函数复制给变量,为其设置属性,调用其方法,<br>
			当嵌套定义函数时,就构成了闭包<br>
			关键字this没有作用域,嵌套函数作为方法调用时,this指向调用它的对象,<br>
			如果作为函数调用,this值要么是全局对象要么是undefined,不会指向外层函数的上下文,<br>
			如果要使用外层函数的上下文,应该先将其保存到变量中<br>
			函数参数<br>
			当实际传入参数必定以的参数少时,其余参数都将置为undefined<br>
			作为命名空间的函数,不在任何函数内定义的变量是全局变量<br>
			闭包：从技术角度讲javascript中的函数都是闭包<br>
			javascript函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的<br>
			实现闭包：cpu架构(如果一个函数的局部变量定义在cpu的栈中,那么当函数返回时它们的确就不存在了)
			而作用域链更像是一个对象列表,而不是绑定的栈,<br>
			高阶函数:调用函数的函数<br>
			？？？不完全函数<br>
			
			
			注意必要时添加实参类型检查逻辑<br>
		</p>
		<script type="text/javascript">
			//注意这里只是把函数赋值给一个变量,而不是把返回值给变量
			var f1 = function(x){return x*x;};
			console.log(f1);//function()
			var v1 = (function(x){return x*x;}(2));//调用 
			console.log(f1(3));//9
			console.log(v1);//4
			//带函数名的函数[用于递归非常方便]
			function f2(x){
				if(x<=1) return 1;
				return x * f2(x-1);
			}
			console.log(f2(3));
			//查看当前脚本运行时是否是严格模式
			var strict = (function(){return !this;}());
			console.log("严格模式?"+strict);
			//this
			var o1 = {
				m:function(){
					var self = this;
					console.log("外层函数的this: "+this);
					console.log(this==o1);//true
					f();
					function f(){
						console.log("嵌套函数的this: "+this);
						console.log(this==o1);//false
						console.log(self==o1);//true
					};
				}
			};
			o1.m();
			//arguments表示实际传入参数 
			function max(){
				var max = Number.NEGATIVE_INFINITY;
				for(var i = 0;i<arguments.length;i++){
					if(arguments[i]>max) max = arguments[i];
				}
				return max;
			}
			console.log(max(1,2,1000,4,3,500));//1000
			//caller和callee属性
			var f3  = function(x){//匿名函数使用递归
				if(x<=1) return 1;
				return x * arguments.callee(x-1);
			};
			console.log(f3(4)); 
			function f4(x){
				console.log(x);
				arguments[0]=null;//会影响实参
				console.log(x);//null
			}
			f4({"1":"a","2":"b"});
			//---
			function flexisum(a){
				var total = 0;
				for(var i=0;i<arguments.length;i++){
					var element = arguments[i], n;
					if(element == null) continue;
					if(Array.isArray(element)){//数组就递归
						n = flexisum.apply(this,element);
					}else if(typeof element == 'function'){//是函数就调用函数并转换返回值
						n = Number(element());
					}else{
						n = Number(element);//强制类型转换
					}
					if(isNaN(n)){//非数字则报错
						throw Error("covert error");
					}
					total += n;
				}
				return total;
			}
			console.log("和："+flexisum([123,321,[1,2,3]]));
			//作为值的函数
			function add(a,b){return a+b;};
			function subtract(a,b){return a-b;};
			function multiply(a,b){return a*b;};
			function divide(a,b){return a/b;};
			function operate1(op,op1,op2){
				return op(op1,op2);
			}
			console.log(operate1(add,add(2,5),multiply(2,3)));//13
			var obj1 = {
				add:function(a,b){return a+b;},
				subtract:function(a,b){return a-b;},
				multiply:function(a,b){return a*b;},
				divide:function(a,b){return a/b;},
				pow:Math.pow
			};
			function operate2(op,op1,op2){
				if(typeof obj1[op]==="function"){
					return obj1[op](op1,op2);
				}else{
					throw "unknow opt";
				}
			}
			console.log(operate2("add","hello",operate2("add"," ","world")));//hello world
			//将自身当做数组来用,缓存上一次计算结果
			function factorial(n){
				console.log("out n:"+n);
				if(isFinite(n) && n>0 && n==Math.round(n)){
					if(!(n in factorial)){
						factorial[n] = n * factorial(n-1);
					}
					console.log("n:"+n+" - "+factorial[n]);
					return factorial[n];
				}else{
					return NaN;
				}
			};
			factorial[1] = 1;
			console.log(factorial(4));			
			function f5(x){
				f5[0]++;
				return x+f5[0];
			}
			f5[0] = 1;
			console.log(f5(0));//2
			console.log(f5(0));//3
			//匿名函数命名空间
			(function(){
				//模块代码
			}());
			//闭包
			var scope = "global scope";
			function f6(){
				var scope = "local scope";
				function f6_1(){
					return scope;
				}
				return f6_1();//返回值 
			}
			console.log(f6());//local scope
			function f7(){
				var scope = "local scope";
				function f7_1(){
					return scope;
				}
				return f7_1;//返回一个方法 
			}
			console.log(f7()());//local scope
			function f8(){
				var count = 0;
				return (function f8_1(){return count++;}());
			}
			//当外部函数返回后除了嵌套函数其他任何代码都无法访问它的局部变量
			console.log(f8());
			function f9(){
				var n = 0;
				return {
					count:function(){return n++;},
					reset:function(){n=0;}
				};
			}
			//每次调用f9()都会创建一个新的作用域链和新的私有变量 ,互不影响
			var v2 = f9(),v3 = f9();
			console.log(v2.count());//0
			console.log(v3.count());//0
			v2.reset();
			console.log(v2.count());//0
			console.log(v3.count());//1
			//使用getter/setter
			function f10(n){
			//不用理会警告 - -!
				return {
					get count(){return n++;},
					set count(m){
						if(m>=n){
							n=m;
						}else{
							throw Error("bigger number please!");
						}
					}
				};
			};
			var v4 = f10(1000);
			console.log(v4.count);//1000
			console.log(v4.count);//1001
			v4.count = 2000;
			console.log(v4.count);//2000
// 			v4.count = 0;//Error[设置的值必须大于当前值]
			
			function setget(o, name, predicate){
				var value;
				o["get"+name] = function(){return value;};
				o["set"+name] = function(v){
					if(predicate && !predicate(v)){
						throw Error("error type");
					}else{
						value = v;
					}
				};
			}
			var obj2={};
			setget(obj2,"Name",function(x){return typeof x == "string";});
			obj2.setName("Grant");
			console.log(obj2.getName());//Grant
// 			obj2.setName(obj2);//error type
			function f11(){
				var funcs1 = [];
				for(var i=0;i<10;i++){
					funcs1[i] = function(){return i;};
				}
				return funcs1;
			}
			//f11中循环生产了10个闭包,关联到闭包的作用域链是"活动的",所以当真正调用的时候i为10,被所有闭包共享
			//显然这不是我们期望的
			console.log(f11()[5]()); //10
			function f12(v){
				return function(){return v;};
			}
			var funcs2 = [];
			for(var i=0;i<10;i++){
				funcs2[i] = f12(i);
			}
			console.log(funcs2[5]());//5
			console.log(f12.length);//函数的length表示形参数量 
			//call()/apply(),它们的第一个实参是要调用函数的母对象
			//call(obj,1,2,...)/apply(o,[1,2,...])后续参数传递方式不同 
			var obj3;
			function f13(v){
				return v;
			}
			console.log(f13.call(obj3,3));var obj3;
			function f13(v){
				return v;
			}
			console.log("call "+f13.call(obj3,3));
			console.log("apply "+f13.apply(obj3,[3]));
			console.log("max:"+Math.max.apply(obj3,[1,2,3,5,4]));
			//Function构造函数,最好一个参数为函数体,Function定义的函数是全局函数
			var f14 = new Function("x","y","return x*y");
			//等价于
// 			function f14(x,y){return x*y;};
			console.log(f14(2,3));
			//标准差:数组中每个值和平均值的差的平方和
			var arr1 = [1,2,3,41,11];
			function sum(x,y){return x+y;};
			function square(x){return x*x;};
			var avg = arr1.reduce(sum)/arr1.length;
			console.log("avg:"+avg);
			var dev = arr1.map(function(x){return x-avg;});
			console.log("dev:"+dev);
			var v5 = Math.sqrt(dev.map(square).reduce(sum)/(arr1.length-1));
			console.log("v5:"+v5);
			//记忆
			function memorize(f){
				var cache = {};
				return function(){
					var key = arguments.length + Array.prototype.join.call(arguments,",");
					if(key in cache){
						return cache[key];
					}else{
						return cache[key] = f.apply(this,arguments);
					}
				};
			}
			//MAX公约数
			function gcd(a,b){
				var t;
				if(a<b){
					t=b,b=a,a=t;
				}
				while(b!=0){
					t=b,b=a%b,a=t;
				}
				return a;
			}
			var gcdmemo = memorize(gcd);
			console.log(gcdmemo(12,16));
			
			
		</script>
	</body>

</html>